package bidserver.src;

import java.io.*;
import java.net.*;
import java.text.ParseException;
import java.util.*;

public class BidClient {
	LinkedList<BidServerConnection> bidServers = new LinkedList<BidServerConnection>();
	public boolean init(){
		return loadConfig("/tmp/bidServer.conf");
	}
	public boolean loadConfig(String conf) {
		try {
			BufferedReader din = new BufferedReader(new FileReader(conf));
			while (true) {
				String line = din.readLine();
				if( line == null)
					break;
				line = line.trim();
				if (line.isEmpty())
					continue; // ignore empty.
				if (line.startsWith("#"))
					continue;// comment.

				// ID Name/IP ServerPort ClientPort
				String s[] = line.split("\\s+");
				if (s.length < 4)
					return false;
				String serverName = new String(s[1]);
				int cPort = Integer.parseInt(s[3]);
				bidServers.addLast(new BidServerConnection(serverName, cPort));
			}
			return true;
		} catch (FileNotFoundException e) {
			return false;
		} catch (IOException e) {
			return false;
		}
	}
	public BidServerConnection connect(){
		Random random = new Random();
		int start = random.nextInt(bidServers.size());
		int i = start; 
		do{
			try {
				Socket socket = new Socket();
				BidServerConnection bidServer = bidServers.get(i);
				InetAddress addr = InetAddress.getByName(bidServer.name);
				InetSocketAddress destAddr = new InetSocketAddress(addr, bidServer.port);
				Util.println("connecting to " + i + " -> " + bidServer.name + ":" + bidServer.port);
				socket.connect(destAddr, Constants.CONNECTION_TIMEOUT);
				Util.println("Connected to " + bidServer.name + ":" + bidServer.port);
				bidServer.socket = socket;
				bidServer.din = new BufferedReader(new InputStreamReader(socket.getInputStream()));
				bidServer.pout = new PrintWriter(socket.getOutputStream());
				return bidServer;
			} catch(SocketTimeoutException e){
				Util.println("\tConnecting to " + i + "timeout.");
			} catch (IOException e) {
				Util.println("\tFailed to connect to " + i);
			}
			i = (i+1)%bidServers.size();
		} while(i != start);
		Util.println("no bid server available.");
		return null;
	}
	public void disconnect(BidServerConnection conn){
		if( conn.socket != null){
			try {
				conn.socket.close();
			} catch (IOException e) {
			}
			conn.socket = null;
			conn.pout = null;
			conn.din = null;
		}
	}
	public LinkedList<Integer> getItemIds(BidServerConnection conn){
		try {
			Msg oMsg = new Msg();
			oMsg.add("Request", "ItemIds");
			Util.sendMsg(oMsg, conn.pout);
			String s = conn.din.readLine();
			if( s == null )
				return null;
			Msg iMsg = new Msg();
			iMsg.fromString(s);
			LinkedList<Integer> itemIds = new LinkedList<Integer>();
			for(TaggedValue tv: iMsg.data)
				if( tv.tag.equals("ItemId"))
					itemIds.addLast(Integer.parseInt(tv.value));
			return itemIds;
		} catch (IOException e) {
		}
		return null;
	}
	public Bid getItemTopBid(BidServerConnection conn, int itemId){
		try {
		Msg oMsg = new Msg();
		oMsg.add("Request", "ItemTopBid");
		oMsg.add("ItemId", itemId);
		Util.sendMsg(oMsg, conn.pout);
		Msg iMsg = new Msg();
		String s = conn.din.readLine();
		if( s == null )
			return null;
		iMsg.fromString(s);
		if( iMsg.getValue("Bid") == null )
			return null;//no bid.
		Bid bid = new Bid(Integer.parseInt(iMsg.getValue("Bid")), iMsg.getValue("Bidder"), 
				Util.dateFormat.parse(iMsg.getValue("BidTime")));
		return bid;
		} catch (IOException e) {
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	public ItemInfo getItemInfo(BidServerConnection conn, int itemId){
		try {
		Msg oMsg = new Msg();
		oMsg.add("Request", "ItemInfo");
		oMsg.add("ItemId", itemId);
		Util.sendMsg(oMsg, conn.pout);
		Msg iMsg = new Msg();
		String s = conn.din.readLine();
		if( s == null)
			return null;
		iMsg.fromString(s);
		ItemInfo itemInfo = new ItemInfo(itemId, iMsg.getValue("ItemTitle"), iMsg.getValue("ItemDesc"), 
				Util.dateFormat.parse(iMsg.getValue("AuctionEndTime")));
		return itemInfo;
		} catch (IOException e) {
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	public  LinkedList<Bid> getBids(BidServerConnection conn, int itemId){//bid history
		try {
			Msg oMsg = new Msg();
			oMsg.add("Request", "ItemBids");
			oMsg.add("ItemId", itemId);
			Util.sendMsg(oMsg, conn.pout);
			String s = conn.din.readLine();
			if( s == null)
				return null;
			Msg iMsg = new Msg();
			iMsg.fromString(s);
			LinkedList<Bid> bids = new LinkedList<Bid>();
			Bid bid = null;
			for(TaggedValue tv : iMsg.data){
				if( tv.tag.equals("Bid")) {
					bid = new Bid();
					bids.addLast(bid);
					bid.bid = Integer.parseInt(tv.value);
				}
				else if( tv.tag.equals("Bidder"))
					bid.bidder = tv.value;
				else if( tv.tag.equals("BidTime"))
					bid.bidTime = Util.dateFormat.parse(tv.value);
			}
			return bids;
		} catch (IOException e) {
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	public Bid placeBid(BidServerConnection conn, int itemId, int price, String bidder, Date bidTime){
		try {
			Msg oMsg = new Msg();
			oMsg.add("Request", "PlaceBid");
			oMsg.add("ItemId", itemId);
			oMsg.add("Bid", price);
			oMsg.add("Bidder", bidder);
			oMsg.add("BidTime", bidTime);
			Util.sendMsg(oMsg, conn.pout);
			String s = conn.din.readLine();
			if( s == null )
				return null;
			Msg iMsg = new Msg();
			iMsg.fromString(s);
			Bid bid = new Bid(Integer.parseInt(iMsg.getValue("Bid")), iMsg.getValue("Bidder"), Util.dateFormat.parse(iMsg.getValue("BidTime")));
			return bid;
		} catch (IOException e) {
		} catch (NumberFormatException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
		return null;
	}
	
	//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Test @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
	
	public static void main(String[] args) {
		BidClient client = new BidClient();
		if( !client.init())
			return;
		
		BidServerConnection conn = client.connect();
		try {
			BufferedReader din = new BufferedReader(new InputStreamReader(System.in));
			while(true){
				String line = din.readLine();
				if( line.isEmpty() )
					continue;
				
				String cmd[] = line.split("\\s+");
				if( cmd[0].equals("quit"))
					break;
				else if( cmd[0].endsWith("help")){
					Util.println("commands:\n\thelp - get all usage of commands.\n" +
							"\tquit - exit the program\n" +
							"\tconnect - connect to a random server\n" +
							"\tstress <count>\n" +
							"\tgetAllItems - get a list of items with top bids to bid\n" +
							"\tgetBids <itemId> - get the bid history of itemId\n" +
							"\tbid <itemId> <price> <name> - bid on itemId.");
				}
				else if( cmd[0].equals("connect")){
					if( conn != null)
					client.disconnect(conn);
					conn = client.connect();
				}
				else if( cmd[0].equals("getAllItems")){
					if( conn == null ){
						Util.println("no connection.");
						continue;
					}
					
					LinkedList<Integer> itemIds = client.getItemIds(conn);
					if( itemIds != null){
						for(int i:itemIds){
							ItemInfo itemInfo = client.getItemInfo(conn, i);
							if( itemInfo == null)
								continue;
							Bid topBid = client.getItemTopBid(conn, i);
							Util.print(i + "\t" + itemInfo.itemTitle + "\t" + itemInfo.itemDesc + "\t" + Util.dateFormat.format(itemInfo.auctionEndTime));		
							if( topBid != null ){
								Util.print("\t" + topBid.bid/100 + "." + topBid.bid%100  + "\t" + topBid.bidder + "\t" + Util.dateFormat.format(topBid.bidTime));
							}
							Util.println("");
						}
					}
				}
				else if( cmd[0].equals("getBids")){
					if( cmd.length < 2){
						Util.println("usage: getBids <itemId>");
						continue;
					}
					if( conn == null ){
						Util.println("no connection.");
						continue;
					}
					
					int itemId = Integer.parseInt(cmd[1]);
					LinkedList<Bid> bids = client.getBids(conn, itemId);
					if( bids == null)
						continue;
					for( Bid bid : bids){
						Util.println(bid.bid/100 + "." + bid.bid%100 + "\t" + bid.bidder + "\t" + Util.dateFormat.format(bid.bidTime));
					}
				}
				else if( cmd[0].equals("bid")){
					if( cmd.length < 4 ){
						Util.println("usage: bid <itemId> <price> <name>");
						continue;
					}
					if( conn == null ){
						Util.println("no connection.");
						continue;
					}
					
					int itemId = Integer.parseInt(cmd[1]);
					Double p = new Double(100 * Double.parseDouble(cmd[2]));
					int price = p.intValue();
					String name = cmd[3];
					Date bidTime = Calendar.getInstance().getTime(); 
					Bid topBid = client.placeBid(conn, itemId, price, name, bidTime);
					if( topBid == null)
						continue;
					Util.println(topBid.bid/100 + "." + topBid.bid%100 + "\t" + topBid.bidder + "\t" + Util.dateFormat.format(topBid.bidTime));
				} else if(cmd[0].equals("stress")){
					if( cmd.length < 2 ){
						Util.println("usage: stress <count>");
						continue;
					}
					if( conn == null ){
						Util.println("no connection.");
						continue;
					}
					int count = Integer.parseInt(cmd[1]);
					for(int j=0; j<count; j++ ){
						LinkedList<Integer> itemIds = client.getItemIds(conn);
						if( itemIds != null){
							for(int i:itemIds){
								ItemInfo itemInfo = client.getItemInfo(conn, i);
								if( itemInfo == null)
									continue;
								Bid topBid = client.getItemTopBid(conn, i);
								Util.print(i + "\t" + itemInfo.itemTitle + "\t" + itemInfo.itemDesc + "\t" + Util.dateFormat.format(itemInfo.auctionEndTime));		
								if( topBid != null ){
									Util.print("\t" + topBid.bid/100 + "." + topBid.bid%100  + "\t" + topBid.bidder + "\t" + Util.dateFormat.format(topBid.bidTime));
								}
								Util.println("");
								//Bid it.
								Date bidTime = Calendar.getInstance().getTime(); 
								client.placeBid(conn, i, topBid == null ? 1 : topBid.bid+1, "jason", bidTime);
								//Get Bid history.
								LinkedList<Bid> bids = client.getBids(conn, i);
								if( bids == null)
									continue;
								for( Bid bid : bids){
									Util.println(bid.bid/100 + "." + bid.bid%100 + "\t" + bid.bidder + "\t" + Util.dateFormat.format(bid.bidTime));
								}
								
							}
						}
					}
					
				} else {
					Util.println("command not expected.");
				}
				
			}						
		} catch (IOException e) {
			e.printStackTrace();					
	}
}

}
